# -*- coding: utf-8 -*-

import random

from functions import *
import lts

def _sync_tow_random (system, sync_set, verbose=False):
	if len(system) > 1:
		s1 = system.pop(int((random.random()*10) % len(system)))
		s2 = system.pop(int((random.random()*10) % len(system)))
		system = system + [s1.sync(s2,sync_set)]
		if verbose:
			print "Synchronizing:",s1.name,"||",s2.name
	return system

def _sync_pairs_random (system, sync_set, verbose=False):
	new_system = []
	while len(system) > 1:
		s1 = system.pop(int((random.random()*10) % len(system)))
		s2 = system.pop(int((random.random()*10) % len(system)))
		new_system += [s1.sync(s2, sync_set)]
		if verbose:
			print "Synchronizing:",s1.name,"||",s2.name
	new_system += system
	return new_system

def _sync_diag_pairs (system, sync_set, verbose=False):
	# Hago una copia de system.
	old_system = list(system)
	#  Lista de subsistemas diagnosticables.
	diag_subsystems = [s for s in system if s.isdiag]
	# Lista de subsystemas no diagnosticables.
	nondiag_subsystems = [s for s in system if not s.isdiag]
	# Sincronizo de a paraes en cada lista.
	new_system = _sync_pairs_random(diag_subsystems, sync_set, verbose)
	new_system += _sync_pairs_random(nondiag_subsystems, sync_set, verbose)
	if set(old_system) == set(new_system):
		return _sync_pairs_random (system, sync_set, verbose)
	else:
		return new_system
	
def _sync_no_sharing (system, sync_set, verbose=False):
	# Hacemos una copia de system.
	old_system = list(system)
	# Sistema resultante.
	new_system = []
	# Clasifico los subsistemas que no comparten eventos de sincronizacion.
	subsystem_classes = [[]]
	while system:
		s1 = system.pop()
		for xs in subsystem_classes:
			if len(xs) == 0:
				xs.append(s1)
				subsystem_classes.append([])
				break
			share_event = False
			for s2 in xs:
				if share_events(s1,s2,sync_set):
					share_event = True
					break
			if not share_event:
				xs.append(s1)
				break
	# Sicronizo de a pares los subsistemas de cada clase.
	subsystem_classes.remove([])
	for xs in subsystem_classes:
		new_system += _sync_pairs_random(xs, sync_set, verbose)
	# Si el nuevo sistema es igual al anterior es porque no hubo sincronizacion.
	if set(old_system) == set(new_system):
		# Si estoy en modo verboso informo.
		if verbose:
			print "The heuristic 3 couldn't make any composition."
			print "Changing to heuristric 4.\n"
		# Intento con otra heuristica de sincronizacion.
		return _sync_partial_sharing(old_system, sync_set, verbose)
	else:
		# Si no son iguales devuelvo el nuevo sistema.
		return new_system

def _sync_full_sharing (system, sync_set, verbose=False):
	# Hacemos una copia de system.
	old_system = list(system)
	# Sistema resultante.
	new_system = []
	# Clasifico los subsistemas que no comparten eventos de sincronizacion.
	subsystem_classes = [[]]
	while system:
		s1 = system.pop()
		for xs in subsystem_classes:
			if len(xs) == 0:
				xs.append(s1)
				subsystem_classes.append([])
				break
			share_all_event = True
			for s2 in xs:
				if not share_all_events(s1,s2,sync_set):
					share_all_event = False
					break
			if share_all_event:
				xs.append(s1)
				break
	# Sicronizo de a pares los subsistemas de cada clase.
	subsystem_classes.remove([])
	for xs in subsystem_classes:
		new_system += _sync_pairs_random(xs, sync_set, verbose)
	# Si el nuevo sistema es igual al anterior es porque no hubo sincronizacion.
	if set(old_system) == set(new_system):
		# Si estoy en modo verboso informo.
		if verbose:
			print "The heuristic 5 couldn't make any composition."
			print "Changing to heuristric 4.\n"
		# Intento con otra heuristica de sincronizacion.
		return _sync_partial_sharing(old_system, sync_set, verbose)
	else:
		# Si no son iguales devuelvo el nuevo sistema.
		return new_system

def _sync_partial_sharing (system, sync_set, verbose=False):
	# Hacemos una copia de system.
	old_system = list(system)
	# Sistema resultante.
	new_system = []
	# Clasifico los subsistemas que no comparten eventos de sincronizacion.
	subsystem_classes = [[]]
	while system:
		s1 = system.pop()
		for xs in subsystem_classes:
			if len(xs) == 0:
				xs.append(s1)
				subsystem_classes.append([])
				break
			share_event = True
			for s2 in xs:
				if not share_events(s1,s2,sync_set):
					share_event = False
					break
			if share_event:
				xs.append(s1)
				break
	# Sicronizo de a pares los subsistemas de cada clase.
	subsystem_classes.remove([])
	for xs in subsystem_classes:
		new_system += _sync_pairs_random(xs, sync_set, verbose)
	# Si el nuevo sistema es igual al anterior es porque no hubo sincronizacion.
	if set(old_system) == set(new_system):
		# Si estoy en modo verboso informo.
		if verbose:
			print "The heuristic 4 couldn't make any composition."
			print "Changing to heuristric 3.\n"
		# Intento con otra heuristica de sincronizacion.
		return _sync_no_sharing(old_system, sync_set, verbose)
	else:
		# Si no son iguales devuelvo el nuevo sistema.
		return new_system

def _sync_sequential (system, sync_set, verbose=False):
	# Compongo los subsistemas de manera secuencial.
	if verbose:
		print "Synchronizing:", \
			  ('||').join([s.name.replace('.aut','') for s in system])
	s1 = system.pop(0)
	while system:
		s2 = system.pop(0)
		s1 = s1.sync(s2, sync_set)
	return [s1]

heuristics = dict()
heuristics[0] = _sync_tow_random
heuristics[1] = _sync_pairs_random
heuristics[2] = _sync_diag_pairs
heuristics[3] = _sync_no_sharing
heuristics[4] = _sync_partial_sharing
heuristics[5] = _sync_full_sharing
heuristics[6] = _sync_sequential

def synchronize (system, sync_set, level=0, verbose=False):
	"""
		Descripcion:
			Toma un conjunto de subsistemas y realiza composisciones sobre estos
			segun el nivel de sincronizacion especificado.
		Argumentos:
			system	 -	Instancia de la clase Set() que contiene el conjunto de 
						subsistemas a componer. Cada subsistema debe ser una
						instancia de la clase lts.LTS().
			sync_set -	Instancia de la clase Set() que contiene el conjunto de 
						eventos en los cuales se quiere sincronizar.
			level	 -	Es un numero entre 0 y 6 que indica el nivel (tipo) de 
						composicion a utilizar. Por defecto su valor es 0.
			verbose	 -	Es un booleano que dice si se debe ejecutar la funcion 
						en modo verboso o no.
		Retorno:
			Instancia de la clase Set() que contiene el nuevo conjunto de 
			subsistemas (instancias de la clase lts.LTS()) luego de realizar la 
			o las composiciones necesarias.
	"""
	if verbose:
		print ""
	return heuristics[level](system, sync_set, verbose)
